<?php
/**
 *
 */
namespace core;
class App {
    public static $namespace = 'app';

    /**
     * @var bool 是否初始化过
     */
    protected static $init = false;

    /**
     * @var array 请求调度分发
     */
    protected static $dispatch;

    /**
     * @var bool 应用路由检测
     */
    protected static $routeCheck;

    /**
     * @var string 当前模块路径
     */
    public static $modulePath;

    /**
     * @var bool 严格路由检测
     */
    protected static $routeMust;

    /**
     * @var bool 应用类库后缀
     */
    public static $suffix = false;

    public static function run(Request $request = null) {
        $request = is_null($request) ? Request::instance() : $request;
        //加载配置项 注册app命名空间
        $config = self::initCommon();
        // 获取应用调度信息
        $dispatch = self::$dispatch;
        // 未设置调度信息则进行 URL 路由检测
        if (empty($dispatch)) {
            $dispatch = self::routeCheck($request, $config);
        }
        // 记录当前调度信息
        $request->dispatch($dispatch);
        $data = self::exec($dispatch, $config);
        // 清空类的实例化
        Loader::clearInstance();
        // 输出数据到客户端
        if ($data instanceof Response) {
            $response = $data;
        } elseif (!is_null($data)) {
            // 默认自动识别响应输出类型
            $type = $request->isAjax() ?
                Config::get('default_ajax_return') :
                Config::get('default_return_type');

            $response = Response::create($data, $type);
        } else {
            $response = Response::create();
        }
        return $response;
    }

    /**
     * 执行调用分发
     * @access protected
     * @param array $dispatch 调用信息
     * @param array $config 配置信息
     * @return Response|mixed
     * @throws \InvalidArgumentException
     */
    protected static function exec($dispatch, $config) {
        switch ($dispatch['type']) {
            case 'redirect': // 重定向跳转
                //                $data = Response::create($dispatch['url'], 'redirect')
                //                    ->code($dispatch['status']);
                break;
            case 'module': // 模块/控制器/操作
                $data = self::module(
                    $dispatch['module'],
                    $config,
                    isset($dispatch['convert']) ? $dispatch['convert'] : null
                );
                break;
            case 'controller': // 执行控制器操作
                //                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                //                $data = Loader::action(
                //                    $dispatch['controller'],
                //                    $vars,
                //                    $config['url_controller_layer'],
                //                    $config['controller_suffix']
                //                );
                break;
            case 'method': // 回调方法
                //                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                //                $data = self::invokeMethod($dispatch['method'], $vars);
                break;
            case 'function': // 闭包
                //                $data = self::invokeFunction($dispatch['function']);
                break;
            case 'response': // Response 实例
                //                $data = $dispatch['response'];
                break;
            default:
                '';
        }
        return $data;
    }

    /**
     * 执行模块
     * @access public
     * @param array $result 模块/控制器/操作
     * @param array $config 配置参数
     * @param bool $convert 是否自动转换控制器和操作名
     * @return mixed
     * @throws HttpException
     */
    public static function module($result, $config, $convert = null) {
        if (is_string($result)) {
            $result = explode('/', $result);
        }

        $request = Request::instance();

        if ($config['app_multi_module']) {
            // 多模块部署
            $module = strip_tags(strtolower($result[0] ?: $config['default_module']));
            $bind = Route::getBind('module');
            $available = false;
            if ($bind) {
                // 绑定模块
                //                list($bindModule) = explode('/', $bind);
                //
                //                if (empty($result[0])) {
                //                    $module    = $bindModule;
                //                    $available = true;
                //                } elseif ($module == $bindModule) {
                //                    $available = true;
                //                }
            } elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
                //不在禁止访问模块
                $available = true;
            }
            // 模块初始化
            if ($module && $available) {
                // 初始化模块
                $request->module($module);
                $config = self::init($module);
            } else {

            }
        } else {
            // 单一模块部署
            $module = '';
            $request->module($module);
        }

        // 当前模块路径
        App::$modulePath = APP_PATH . ($module ? $module . DS : '');
        // 是否自动转换控制器和操作名
        $convert = is_bool($convert) ? $convert : $config['url_convert'];
        // 获取控制器名
        $controller = strip_tags($result[1] ?: $config['default_controller']);
        $controller = $convert ? strtolower($controller) : $controller;
        // 获取操作名
        $actionName = strip_tags($result[2] ?: $config['default_action']);
        $actionName = $convert ? strtolower($actionName) : $actionName;
        // 设置当前请求的控制器、操作
        $request->controller(Loader::parseName($controller, 1))->action($actionName);
        try {
            $instance = Loader::controller(
                $controller,
                $config['url_controller_layer'],
                $config['controller_suffix'],
                $config['empty_controller']
            );
        } catch (\Exception $e) {

        }
        // 获取当前操作名
        $action = $actionName . $config['action_suffix'];

        $vars = [];
        if (is_callable([$instance, $action])) {
            // 执行操作方法
            $call = [$instance, $action];
            // 严格获取当前操作方法名
            $reflect = new \ReflectionMethod($instance, $action);
            $methodName = $reflect->getName();
            $suffix = $config['action_suffix'];
            $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
            $request->action($actionName);
        } elseif (is_callable([$instance, '_empty'])) {
            // 空操作
            $call = [$instance, '_empty'];
            $vars = [$actionName];
        } else {
            // 操作不存在
            //            throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
        }
        return self::invokeMethod($call, $vars);
    }

    /**
     * 调用反射执行类的方法 支持参数绑定
     * @access public
     * @param string|array $method 方法
     * @param array $vars 变量
     * @return mixed
     */
    public static function invokeMethod($method, $vars = []) {
        if (is_array($method)) {
            $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
            $reflect = new \ReflectionMethod($class, $method[1]);
        } else {
            // 静态方法
            $reflect = new \ReflectionMethod($method);
        }
        $args = self::bindParams($reflect, $vars);
        return $reflect->invokeArgs(isset($class) ? $class : null, $args);
    }

    /**
     * URL路由检测（根据PATH_INFO)
     * @access public
     * @param  \core\Request $request 请求实例
     * @param  array $config 配置信息
     * @return array
     */
    public static function routeCheck($request, array $config) {
        $path = $request->path();
        $depr = $config['pathinfo_depr'];
        $result = false;
        // 路由检测
        $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
        if ($check) {
            // 路由检测（根据路由定义返回不同的URL调度）
            $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
            $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
            if ($must && false === $result) {
                // 路由无效

            }
        }

        // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
        if (false === $result) {
            $result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
        }
        return $result;
    }

    public static function initCommon() {
        if (empty(self::$init)) {
            Loader::addNamespace(self::$namespace, ROOT_PATH . 'app' . DS);
            //加载初始化文件
            self::init();
            self::$init = true;
        }
        return Config::get();
    }

    public static function init($module = '') {
        // 定位模块目录
        $module = $module ? $module . DS : '';

        // 加载初始化文件
        if (is_file(APP_PATH . $module . 'init' . EXT)) {
            include APP_PATH . $module . 'init' . EXT;
        } else {
            // 加载模块配置
            $config = Config::load(APP_PATH . $module . 'config' . EXT);
            // 读取数据库配置文件
            $filename = APP_PATH . $module . 'database' . EXT;
            Config::load($filename, 'database');
            // 加载公共文件
            $path = APP_PATH . $module;
            if (is_file($path . 'common' . EXT)) {
                include $path . 'common' . EXT;
            }
        }
        return Config::get();
    }

    /**
     * 调用反射执行类的实例化 支持依赖注入
     * @access public
     * @param string $class 类名
     * @param array $vars 变量
     * @return mixed
     */
    public static function invokeClass($class, $vars = []) {
        $reflect = new \ReflectionClass($class);
        $constructor = $reflect->getConstructor();
        $args = $constructor ? self::bindParams($constructor, $vars) : [];
        return $reflect->newInstanceArgs($args);
    }

    /**
     * 绑定参数
     * @access private
     * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
     * @param array $vars 变量
     * @return array
     */
    private static function bindParams($reflect, $vars = []) {
        // 自动获取请求变量
        if (empty($vars)) {
            $vars = Config::get('url_param_type') ?
                Request::instance()->route() :
                Request::instance()->param();
        }

        $args = [];
        if ($reflect->getNumberOfParameters() > 0) {
            // 判断数组类型 数字数组时按顺序绑定参数
            reset($vars);
            $type = key($vars) === 0 ? 1 : 0;

            foreach ($reflect->getParameters() as $param) {
                $args[] = self::getParamValue($param, $vars, $type);
            }
        }

        return $args;
    }

    /**
     * 获取参数值
     * @access private
     * @param \ReflectionParameter $param 参数
     * @param array $vars 变量
     * @param string $type 类别
     * @return array
     */
    private static function getParamValue($param, &$vars, $type) {
        $name = $param->getName();
        $class = $param->getClass();

        if ($class) {
            $className = $class->getName();
            $bind = Request::instance()->$name;

            if ($bind instanceof $className) {
                $result = $bind;
            } else {
                if (method_exists($className, 'invoke')) {
                    $method = new \ReflectionMethod($className, 'invoke');

                    if ($method->isPublic() && $method->isStatic()) {
                        return $className::invoke(Request::instance());
                    }
                }

                $result = method_exists($className, 'instance') ?
                    $className::instance() :
                    new $className;
            }
        } elseif (1 == $type && !empty($vars)) {
            $result = array_shift($vars);
        } elseif (0 == $type && isset($vars[$name])) {
            $result = $vars[$name];
        } elseif ($param->isDefaultValueAvailable()) {
            $result = $param->getDefaultValue();
        } else {
            throw new \InvalidArgumentException('method param miss:' . $name);
        }

        return $result;
    }
}